#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include "task.h"
#include "ipmi.h"
#include "ipmi_lan.h"
#include "task_lan.h"
#include "rmcp.h"

static sockfd = 0;

static void send_packet(payload_info_t* ipmi_info, channel_info_t* channel_info)
{
//    payload_info_t* ipmi_info = (payload_info_t*)payload;
//    channel_info_t* channel_info = (channel_info_t*)channel;
    sendto(sockfd, ipmi_info->data, ipmi_info->data_size, 0, (struct sockaddr*)&channel_info->address, sizeof(channel_info->address));
//    sendto(sd, buffer, bytes, 0, (struct sockaddr*)&addr, sizeof(addr));
}

static int16 process_ping(payload_info_t* req_msg, channel_info_t* channel_info)
{
    int result = TRUE;
    payload_info_t res_msg;
    memcpy(&res_msg, req_msg, sizeof(payload_info_t));
    rmcp_header_t* rmcp_header_req = (rmcp_header_t*)req_msg->data;
    rmcp_header_t* rmcp_header_res = (rmcp_header_t*)&res_msg.data;
    asf_message_t* asf_message_req = (asf_message_t*)(rmcp_header_req+1);
    asf_message_t* asf_message_res = (asf_message_t*)(rmcp_header_res+1);

    if((rmcp_header_req->version & 0x06) != 0x06){
        printf("Version failed\n");
        result = FALSE;
    }

    if((rmcp_header_req->class_of_message & 0x06) != 0x06){
        DISPLAY_0("Class of msg failed\n");
        result = FALSE;
    }

    if((asf_message_req->message_type & 0x80) != 0x80){
        printf("msg type failed:%x\n",asf_message_req->message_type);
        result = FALSE;
    }

    if(result){
        memcpy(rmcp_header_res, rmcp_header_req, sizeof(rmcp_header_t));
        memcpy(&asf_message_res->iana, &asf_message_req->iana, sizeof(&asf_message_req->iana));
        asf_message_res->message_type = 0x40;
        asf_message_res->message_tag = asf_message_req->message_tag;
        asf_message_res->reserved1 = 0x00;
        asf_message_res->data_length = 0x10;
        asf_message_res->iana_oem = 0x0;
        asf_message_res->oem = 0x0;
        asf_message_res->supported_entities = 0x81;
        asf_message_res->supported_interactions = 0x00;
        memset(&asf_message_res->reserved2, 0, sizeof(asf_message_res->reserved2));
        res_msg.data_size = sizeof(rmcp_header_t) + sizeof(asf_message_t);
        printf("response pong\n");
        send_packet(&res_msg, channel_info);
    }
    return result;
}

static int init_socket()
{
    struct sockaddr_in server;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    if(sockfd < 0){
        DISPLAY_1("Create socket failed!!\n");
        return -1;
    }

    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PRIMARY_RMCP_PORT);

    if ( bind(sockfd, (struct sockaddr*)&server, sizeof(server)) < 0 )
        DISPLAY_1("Bind failed\n");

    return 0;
}

static void process_rmcp(payload_info_t* ipmi_info, channel_info_t* channel_info)
{
    if(process_ping(ipmi_info, channel_info)){
        return;
    }
    ipmi_lan_1_5_t* ipmi_1_5_packet = (ipmi_lan_1_5_t*)ipmi_info->data;
    ipmi_lan_2_0_t* ipmi_2_0_packet = (ipmi_lan_2_0_t*)ipmi_info->data;
    channel_info->payload_ipmi20 = FALSE;

    if( (ipmi_1_5_packet->ipmi_header.auth_type & 0x0F) == 0x06 )
        channel_info->payload_ipmi20 = TRUE;

    if(!channel_info->payload_ipmi20){
        printf("TRUE\n");
        ipmi_info->start_of_request = sizeof(ipmi_lan_1_5_t);
        ipmi_info->netfn = ipmi_1_5_packet->ipmi_message.netFn >> 2;
        ipmi_info->cmd = ipmi_1_5_packet->ipmi_message.cmd;
    }else{
        printf("FALSE\n");
        ipmi_info->start_of_request = sizeof(ipmi_lan_2_0_t);
        ipmi_info->netfn = ipmi_2_0_packet->ipmi_message.netFn >> 2;
        ipmi_info->cmd = ipmi_2_0_packet->ipmi_message.cmd;
    }

    ipmi_info->size_of_request = ipmi_info->data_size - ipmi_info->start_of_request - 1;

    printf("Set msg queue!!\n");
    set_message_queue(ipmi_info, channel_info);
}

static void receive_packet()
{
    struct sockaddr_in from;
    int res;
    fd_set readfd, testfd;
    struct timeval timeout;
    int length;
    length = sizeof(struct sockaddr_in);
    payload_info_t ipmi_info;
    channel_info_t channel_info;
    unsigned char *data;
    data = (unsigned char*)&ipmi_info.data;

    init_socket();

    DISPLAY_1("%s\n",__func__);
    while(1){
        memset(&ipmi_info, 0, sizeof(payload_info_t));
        res = recvfrom(sockfd, data, PACKET_BUFFER, 0, (struct sockaddr *)&from, &length);
        if (res <= 0){
            DISPLAY_1("error in recvfrom");
            continue;
        }
        
        channel_info.channel = CHANNEL_LAN;
        channel_info.response.send = &send_rmcp;
        memcpy(&channel_info.address, &from, sizeof(struct sockaddr));
        ipmi_info.data_size = res;
        process_rmcp(&ipmi_info, &channel_info);
    }
}

static task_table_t TaskTable[] = {
    { {0, 0, 0, "rcv_pkt", 0}, -1, &receive_packet},
    { {0, 0, 0, 0, 0}, 0, 0 }
};

void task_lan(void *params)
{
    int i = 0;
    while(1){
        for(i=0;i<ARRAY_SIZEOF(TaskTable);i++){
            CREATE_TASK(i, TaskTable[i], params);
        }
    	sleep(1);
    }
}

static int calculate_checksum(unsigned char* data, unsigned long data_size)
{
    unsigned long i = 0;
    unsigned char checksum = 0x00;
    for(i=0;i<data_size;i++){
        checksum += data[i];
    }
    printf("check=%x\n",checksum);
    data[i] = 0x100 - checksum;
}

void send_rmcp(uint8* payload, uint8* channel)
{
    uint8 tmpAddr = 0x00;
    payload_info_t* ipmi_info = (payload_info_t*)payload;
    channel_info_t* channel_info = (channel_info_t*)channel;
    if(!channel_info->payload_ipmi20){
        ipmi_lan_1_5_t* rmcp = (ipmi_lan_1_5_t*)ipmi_info->data;
        rmcp->ipmi_message.netFn += (1 << 2);
        tmpAddr = rmcp->ipmi_message.dstAddr;
        rmcp->ipmi_message.dstAddr = rmcp->ipmi_message.srcAddr;
        rmcp->ipmi_message.srcAddr = tmpAddr;
        rmcp->ipmi_header.ipmi_payload_len = sizeof(ipmi_lan_message_format_t) + ipmi_info->size_of_request + 1;
        calculate_checksum((unsigned char*)&rmcp->ipmi_message.dstAddr,2);
        calculate_checksum((unsigned char*)&rmcp->ipmi_message.srcAddr, 3 + ipmi_info->size_of_request);
        ipmi_info->data_size = sizeof(ipmi_lan_1_5_t) + ipmi_info->size_of_request + 1;
    }else{
        ipmi_lan_2_0_t* rmcp = (ipmi_lan_2_0_t*)ipmi_info->data;
        rmcp->ipmi_message.netFn += (1 << 2);
        tmpAddr = rmcp->ipmi_message.dstAddr;
        rmcp->ipmi_message.dstAddr = rmcp->ipmi_message.srcAddr;
        rmcp->ipmi_message.srcAddr = tmpAddr;
        rmcp->ipmi_header.ipmi_payload_len = sizeof(ipmi_lan_message_format_t) + ipmi_info->size_of_request + 1;
        calculate_checksum((unsigned char*)&rmcp->ipmi_message.dstAddr,2);
        calculate_checksum((unsigned char*)&rmcp->ipmi_message.srcAddr, 3 + ipmi_info->size_of_request);
        ipmi_info->data_size = sizeof(ipmi_lan_2_0_t) + ipmi_info->size_of_request + 1;
    }
    send_packet(ipmi_info, channel_info);
}

